use core::f32;


fn print2terminal(){
    let a = 9;
    println!("a is {}", a);
    println!("a is {}, a again is {}", a, a);
    println!("a is {0}, a again is {0}", a);
    println!("{{}}");    
}

fn basic_syntax(){

    const A: i32 = 321;
    // let A: u64 =123;  const value can not be asigned again
    let mut a = 123;
    a = 456;

    // shadowing
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is: {}", x);

    let mut s = "123";
    // s = s.len();  can not asign integer to str value 
}

fn data_type(){
    // i8,i16,i32,i64,i128,isize
    // u8,i16,u32,u64,u128,usize
    let decimal = 54_321; //can be separated by '_'
    let hexadecimal = 0xff; //tag by '0x'
    let octal = 0o77; //tag by '0o'
    let binary = 0b1110_0001; //tag by '0b'
    let byte = b'A'; //tag by 'b' and only defined with type u8

    // float point
    let x = 2.0; // f64 by default
    let y: f32 = 3.0; // f32 is supported
    
    // Mathematical operations
    let sum = 5 + 10;
    let difference  = 95.5 - 4.3;
    let product = 4 * 20;
    let quotient = 56.9 / 24.6;
    let remainder = 43 % 5;

    // char --> 4 bytes, stand for Unicode value, witch means chinese is supported.
    
    // other data types
    // tuple
    let tup: (i32, f64, u8) = (500, 6.5, 1);
    let (x,y,z) = tup;
    // array
    let a = [1,2,3,4,5];
    let b = ["jan", "feb", "mar"];
    let c:[i32; 5] = [1,2,3,4,5]; //define c with length=5 and elements_type=i32
    let d = [3;5]; // let d = [3,3,3,3,3];
    
    let first = a[0];
    let second = a[1];
    
    // a[0] = 123;  error! a is immutable
    let mut a = [1,2,3];
    a[0] = 4; // it works!
}

fn functions() {
    fn A(x: i32) {
        println!("functions A(): {}", x);
    }
    A(3);

    // let a = (let b = 5);  "let b = 5" do not return a value, so this assignment is error!
    
    let y = {
        let x = 9;
        x + 1
    };
    A(y);

    fn add(x: i32, y: i32) -> i32{
        return x + y;
    }
    A(add(6,6));
}

fn branch(){
    let a = 5;
    let number = if a > 0 {1} else {-1};
    println!("number:{}", number); 
    if a > 0 {

    }else{
        println!("else");
    }
}

fn loops(){
    let mut i = 1;
    while i != 4 {
        println!("while-loop:{}",i);
        i += 1;
    }
    println!("exit");

    //there is no for(s1,s2,s3) in rust and do-while is unavailable

    //iterator
    let a = [6,7,8];
    for i in a.iter() {
        println!("for-loop:{}",i);
    }
    
    //index
    for i in 0..3 {
        println!("a[{}]:{}",i,a[i]);
    }

    //while(true)
    loop {
        println!("in loop: {}", i);
        i += 1;
        if i>10{
            break;
        }
    };

    let r = loop {
        println!("r loop: {}", i);
        i += 1;
        if i>14{
            break i; //break can return the value 
        }
    };
    println!("r:{}",r);
        
}

fn ownership(){

    // val movement
    let s1 = String::from("rust abc!");
    let s2 = s1; // after this statement, s1 is missing
    // println!("s1: {}",s1); error! s1 is missing!
    
    // val clone
    let s1 = String::from("rust abc!");
    let s2 = s1.clone();
    println!("s1: {}, s2: {}",s1, s2);

    //arguments relationship
    {
        let s = String::from("hello"); 
        // s has been declared
        takes_ownership(s);
        // there is a val-movement operation, so s is missing below        
        let x = 6;
        //x has been declared
        makes_copy(x);
        //x is basic type val, so will be not miss 

        fn takes_ownership(sth: String){
            println!("{}", sth);
        }//release val sth automtically

        fn makes_copy(sint: i32){
            println!("{}", sint);
        }

    }

    //val cite and borrow
    let s1 = String::from("rust abc!");
    let s2 = &s1;
    println!("s1: {}, s2: {}",s1, s2);

    {
        let s1 = String::from("rust abc");
        let s2 = &s1; //borrow!
        let s3 = s1; //move!
        // println!("{}",s2); //error!  s1 has been moved, so s2 is 
    }
    {
        let s1 = String::from("rust abc");
        let mut s2 = &s1; //borrow!
        let s3 = s1; //move!
        s2 = &s3;
        println!("{}",s2); 
    }


    let s1 = String::from("rust abc");
    let s2 = &s1; //borrow!    //similar to "const &" in c++
    // s2.push_str("bbb"); error! s1 is unmutable!

    let mut s1 = String::from("rust abc");
    let s2 = &mut s1; //borrow!
    // s2.push_str("bbb"); //it works!
    // let s3 = &mut s1; //error! second mutable borrow occurs here!
    // println!("{},{}", s2, s3);

    //dangling references is not allowed
    // fn dangle() -> &String{
    //     let s = String::from("dangle....");
    //     &s
    // }

}

fn slice(){
    let s = String::from("abcdefghijklmn");
    let p1 = &s[0..5];
    // let l = s.len();
    // let p2 = &s[5..l];
    let p2 = &s[5..];
    // Note! the slicing operation must return a &(barrow) type   
    // s.push_str("appen!"); //error! it cannot mutate affter being barrowed
    println!("{}={}+{}",s,p1,p2);

    let arr = [0,1,2,3,4,5,6,7,8,9];
    let p = &arr[5..8];

    for i in 0..p.len() {
        println!("p[{}]:{}", i, p[i]);
    }
    
    for i in p.iter() {
        println!("{}",i);
    }
}

fn structure(){
    struct Site{
        domain: String,
        name: String,
        nation: String,
        found: u32
    }//only used for defining type, not for declaration

    let site = Site {
        domain: String::from("cn.bing.com"),
        name: String::from("bing"),
        nation: String::from("china"),
        found:2013
    };
    let site2 = Site {
        domain: String::from("hao123.com"),
        ..site // copy other attributs from "site"
    };

    //tuple structure
    struct Color(u8, u8, u8);
    struct Point(f64, f64);
    
    let black = Color(0,0,0);
    let origin = Point(0.0, 0.0);
    
    println!("black = ({}, {}, {})", black.0, black.1, black.2);
    println!("origin = ({}, {})", origin.0, origin.1);    


    //the ownership of structure
    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32
    }
    let rect1 = Rectangle { width: 30, height: 60 };
    println!("site is {:?}", rect1);
    println!("site is {:#?}", rect1);

    //method of structure
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }

        fn bigger(&self, rect: &Rectangle) -> bool {
            self.area() > rect.area()
        }

        fn create(width: u32, height: u32) -> Rectangle {
            Rectangle { width: width, height }
        }
    }
    let rect2 = Rectangle { width: 30, height: 50 };
    println!("rect1's area is {}", rect1.area());
    println!("rect1 is bigger than rect2: {}", rect1.bigger(&rect2));
    let rect3 = Rectangle::create(50, 90);
    println!("rect3 is {:?}", rect3);

    //unit structure
    struct UnitStruct; //can own no attributes.
}

fn enumType(){
    #[derive(Debug)]
    enum Book {
        Papery(u32), 
        Electronic(String)
    }
    let book = Book::Papery(889);
    println!("{:?}", book);

    #[derive(Debug)]
    enum Book2 {
        Papery{ index: u32 }, 
        Electronic{ url: String }
    }
    let book = Book2::Papery{index: 1001};
    let ebook = Book2::Electronic{url: String::from("http://...")};
    println!("{:?}", ebook);
    

    match book {
        Book2::Papery { index} => {
            println!("Papery book {}", index);
        },
        Book2::Electronic { url } => {
            println!("E-book {}", url);
        }
    }

    let str = "abcd";
    match str {
        "abc" => {
            println!("yes!");
        },
        _ => {
            println!("unexpected thing.");
        }
    }

    // option enum type
    enum Option<T>{
        Some(T),
        None,
    }

    let opt = Option::Some("hello");
    match opt {
        Option::Some(someting) => {
            println!("{}", someting);
        },
        Option::None =>{
            println!("opt is nothing!");
        }
    }

    let t = Some(64);
    match t {
        Some(64) => println!("yes!"),
        _ => println!("No")
    }
    
    let i = 0;
    match i {
        0 => println!("zero"),
        _ => {},
    }

    if let 0 = i {
        println!("zero")
    }else{

    }

    let book = Book2::Papery{index: 1001};
    if let Book2::Electronic{url} = book{
        println!("ebook {}", url);
    }else{
        println!("not ebook!")
    }
}





fn main() {
    println!("Hello, rust!");
    print2terminal();
    basic_syntax();
    data_type();
    functions();
    branch();
    loops();
    ownership();
    slice();
    structure();
    enumType();
}


